/*
 * ULSF Sagittarius IDA 
 * Copyright (C) 2012, John Pritchard, all rights reserved.
 * 
 * This program is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see
 * <http://www.gnu.org/licenses/>.
 */
package sagittarius.airfoil;


/**
 * Instances of this class are produced by {@link ChordIterator} to
 * represent the state of a single chord iteration cycle.
 * 
 * Each chord iteration cycle has one or two vertices.  The interior
 * and exterior edges have one vertex, while all other points in the
 * chord are represented by a pair of (two) vertices.
 * 
 * Each chord pair iteration cycle has two vertices.  These two
 * vertices differ in their range coordinates (as members of top or
 * bottom surfaces), and may differ in their domain coordinates.
 * 
 * The geometry of the subject airfoil, including its domain and
 * range, is defined by the {@link ModelVertices$Kind Geometric Kind}
 * structure.
 * 
 * @see ModelVertices$Kind
 * @see AirfoilVertices
 * @see ChordIterator
 */
public final class ChordVertex
    extends MathAbstract
{
    public enum In {
        Domain, Current, None;
    }


    public final AirfoilVertices airfoil;

    public final Geometry geometry;

    public final int dimension;

    private final float[] list;

    public final int index, index2;
    /**
     * This point in the chord represents a single edge vertex,
     * otherwise a pair of chord vertices.  
     */
    public final boolean edge;

    private ChordIterator chord;


    public ChordVertex(ChordIterator chord, float[] list, int index){
        super();
        this.chord = chord;
        this.airfoil = chord.airfoil;
        this.geometry = this.airfoil.geometry;
        this.dimension = this.geometry.dimension.number;
        this.list = list;
        this.index = index;
        this.edge = (0 == index || index == this.airfoil.ve);
        if (this.edge)
            this.index2 = -1;
        else {
            this.index2 = this.airfoil.index2(this.index);
        }
    }


    /**
     * Search for a point between the current and previous chord
     * points, over the first vertex in each pair.
     * 
     * @param prev In the first cycle of the iteration, the previous
     * member of the iteration is null.  After the first cycle of the
     * iteration, the previous member of the iteration is not null.
     * This argument may be null in the first cycle of the iteration.
     * 
     * @param search Airfoil (chord) domain search point
     */
    public In in(float search){
        ChordVertex prev = this.peek(-1);

        if (null == prev){
            final float[] tE = this.getEdge();

            final float dom = tE[this.geometry.domain()];

            if (EEQ(dom,search))
                return In.Current;
            else
                return In.None;
        }
        else if (this.edge){
            final float[] tE = this.getEdge();

            final float tDom = tE[this.geometry.domain()];

            if (EEQ(tDom,search)){

                return In.Current;
            }
            else {
                final float[] pP = prev.getPair();

                final float pDom = pP[this.geometry.domain()];

                switch(this.geometry.path.order){
                case CW:
                    if (pDom >= search && tDom <= search)
                        return In.Domain;
                    else
                        return In.None;

                case CCW:
                    if (pDom <= search && tDom >= search)
                        return In.Domain;
                    else
                        return In.None;

                default:
                    throw new Error();
                }
            }
        }
        else if (prev.edge){
            final float[] tP = this.getPair();

            final float tDom = tP[this.geometry.domain()];

            if (EEQ(tDom,search)){

                return In.Current;
            }
            else {
                final float[] pE = prev.getEdge();

                final float pDom = pE[this.geometry.domain()];

                switch(this.geometry.path.order){
                case CW:
                    if (pDom >= search && tDom <= search)
                        return In.Domain;
                    else
                        return In.None;

                case CCW:
                    if (pDom <= search && tDom >= search)
                        return In.Domain;
                    else
                        return In.None;

                default:
                    throw new Error();
                }
            }
        }
        else {
            final float[] tP = this.getPair();

            final float tDom = tP[this.geometry.domain()];

            if (EEQ(tDom,search)){

                return In.Current;
            }
            else {
                final float[] pP = prev.getPair();

                final float pDom = pP[this.geometry.domain()];

                switch(this.geometry.path.order){
                case CW:
                    if (pDom >= search && tDom <= search)
                        return In.Domain;
                    else
                        return In.None;

                case CCW:
                    if (pDom <= search && tDom >= search)
                        return In.Domain;
                    else
                        return In.None;

                default:
                    throw new Error();
                }
            }
        }
    }
    public float[] getVertices(){
        if (this.edge)
            return this.getEdge();
        else
            return this.getPair();
    }
    /**
     * Interpolate a set of vertices for the domain argument
     */
    public float[] getVerticesFor(float domain){


        final ChordVertex p = this.peek(-1);

        final Geometry.Interpolation a = this.geometry.interpolate(p,this,domain,this.geometry.path.surface);

        final Geometry.Interpolation b = this.geometry.interpolate(p,this,domain,this.geometry.path.surface.next());

        return this.geometry.toArray(a,b);
    }
    /**
     * @return Interior edge vertex for chord iteration
     * 
     * @see #edge
     * 
     * @exception java.lang.IllegalStateException Calling this method on pair point
     */
    public float[] getEdge()
        throws java.lang.IllegalStateException
    {
        if (this.edge){
            float[] vertex = new float[this.dimension];

            System.arraycopy(this.list,this.airfoil.ve,vertex,0,this.dimension);

            return vertex;
        }
        else {
            throw new IllegalStateException();
        }
    }
    /**
     * @return Pair of vertices for chord iteration
     * 
     * @exception java.lang.IllegalStateException Calling this method on edge point
     */
    public float[] getPair()
        throws java.lang.IllegalStateException
    {
        if (this.edge){

            throw new IllegalStateException();
        }
        else {
            float[] vertices = new float[this.dimension<<1];

            System.arraycopy(this.list,this.index,vertices,0,this.dimension);

            System.arraycopy(this.list,this.index2,vertices,this.dimension,this.dimension);

            return vertices;
        }
    }
    public void destroy(){
        ChordIterator chord = this.chord;
        if (null != chord){
            this.chord = null;
            chord.destroy(this.index);
        }
    }
    public ChordVertex peek(int r){

        return this.chord.peek(this.index+r);
    }
}
